#include "address.h"

#include <string.h>
#include <arpa/inet.h>

#include "sockutil.h"

using namespace std;

namespace tnet
{
    Address::Address(uint16_t port,bool isIpv6)
        : m_isIpv6(isIpv6)
    {
        bzero(&m_addr, sizeof(m_addr));
        bzero(&m_addr6, sizeof(m_addr6));

        if(m_isIpv6)
        {
            m_addr6.sin6_family = AF_INET6; 
            m_addr6.sin6_addr = in6addr_any;
            m_addr6.sin6_port = htons(port);
        }
        else 
        {
            m_addr.sin_family = AF_INET; 
            m_addr.sin_addr.s_addr = htonl(INADDR_ANY);
            m_addr.sin_port = htons(port);
        }
    }

    Address::Address(const string& ip, uint16_t port,bool isIpv6)
        : m_isIpv6(isIpv6)
    {
        bzero(&m_addr, sizeof(m_addr));
        bzero(&m_addr6, sizeof(m_addr6));
        
        if(m_isIpv6)
        {
            m_addr6.sin6_family = AF_INET6;
            m_addr6.sin6_port = htons(port);

            if(inet_pton(AF_INET6, ip.c_str(), &m_addr6.sin6_addr) <= 0)
            {
                //may be domain
                char addr[128] = {0};
                uint32_t ret = SockUtil::getHostByName(ip,addr,sizeof(addr));
                if(ret != uint32_t(-1))
                {
                    inet_pton(AF_INET6, addr, &m_addr6.sin6_addr);
                    return;
                }

                LOG_ERROR("invalid ip %s, use 0.0.0.0 instead", ip.c_str());
                //error, may log later, now use INADDR_ANY
                m_addr6.sin6_addr = in6addr_any;    
            }
        }
        else
        {
            m_addr.sin_family = AF_INET;
            m_addr.sin_port = htons(port);

            if(inet_pton(AF_INET, ip.c_str(), &m_addr.sin_addr) <= 0)
            {
                //may be domain
                uint32_t ret = SockUtil::getHostByName(ip);
                if(ret != uint32_t(-1))
                {
                    m_addr.sin_addr.s_addr = ret;
                    return;
                }

                LOG_ERROR("invalid ip %s, use 0.0.0.0 instead", ip.c_str());
                //error, may log later, now use INADDR_ANY
                m_addr.sin_addr.s_addr = htonl(INADDR_ANY);    
            }
        }
    }

    Address::Address(const struct sockaddr_in& addr)
        : m_isIpv6(false)
    {
        memcpy(&m_addr, &addr, sizeof(addr)); 
    }

    Address::Address(const struct sockaddr_in6& addr)
        : m_isIpv6(true)
    {
        memcpy(&m_addr6, &addr, sizeof(addr)); 
    }  

 
    uint32_t Address::ip() const
    {
        return m_isIpv6 ? 0 : ntohl(m_addr.sin_addr.s_addr);    
    }

    string Address::ipstr() const
    {
        string ipstr;
        if(m_isIpv6)
        {
            char buf[128] = {'\0'};
            inet_ntop(AF_INET6, &m_addr6.sin6_addr, buf, static_cast<socklen_t> (sizeof(buf)));
            ipstr = string(buf, 128);
        }
        else
        {
            char buf[32] = {'\0'};
            inet_ntop(AF_INET, &m_addr.sin_addr, buf, static_cast<socklen_t> (sizeof(buf)));
            ipstr = string(buf, 32);
        }
        return ipstr;
    }

    uint16_t Address::port() const
    {
        return m_isIpv6 ? ntohs(m_addr6.sin6_port) : ntohs(m_addr.sin_port);    
    }
}
